package com.superman.libyuvdemo

import android.content.Context
import android.graphics.Bitmap
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaCodecList
import android.media.MediaFormat
import android.media.MediaMuxer
import android.opengl.Matrix
import android.util.Log
import com.superman.media.es.InputSurface
import com.superman.media.es.TextureRender
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean


class EncodeSurface(val context: Context) {

    private val TAG = EncodeSurface::class.qualifiedName
    private val viewportWidth = 1280
    private val viewportHeight = 720
    private val FRAME_RATE = 30
    private var inputFrameIndex = 0
    private val outputEnd = AtomicBoolean(false)
    private var muxer: MediaMuxer? = null
    private var isMuxerStarted = false
    private var videoTrackIndex = -1
    private var encodedFrameIndex = 0
    private val NUM_FRAMES = FRAME_RATE * 5
    private var encoder: MediaCodec? = null
    private var inputSurface: InputSurface? = null


    private var render: TextureRender? = null


    fun encodeUsingEGLAndSurface() {

        val mimeType = MediaFormat.MIMETYPE_VIDEO_AVC
        val format = MediaFormat.createVideoFormat(mimeType, viewportWidth, viewportHeight)
        val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
        val encodeCodecName = codecList.findEncoderForFormat(format)
        encoder = MediaCodec.createByCodecName(encodeCodecName)

        encoder!!.setCallback(object : MediaCodec.Callback() {
            override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {

            }

            override fun onOutputBufferAvailable(
                codec: MediaCodec,
                index: Int,
                info: MediaCodec.BufferInfo
            ) {
//                Log.d(TAG, "encoder output buffer available:$index")
                val isDone = (info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0
                if (isDone || encodedFrameIndex == NUM_FRAMES) {
                    outputEnd.set(true)
                    info.size = 0
                    stop()
                    return
                }
                if (info.size > 0) {
                    if (!isMuxerStarted) {
                        throw RuntimeException("muxer hasn't started");
                    }
                    val encodedData = codec.getOutputBuffer(index)
                    encodedData?.position(info.offset)
                    encodedData?.limit(info.offset + info.size)

                    muxer?.writeSampleData(videoTrackIndex, encodedData!!, info)
                    Log.d(
                        TAG,
                        "encoder output info.offset:${info.offset},size:${info.size},pts:${info.presentationTimeUs}"
                    )

                    codec.releaseOutputBuffer(index, false)
                    ++encodedFrameIndex
                }
            }

            override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
                e.printStackTrace()
            }

            override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {
                Log.d(TAG, "encoder output format changed:$format")
                videoTrackIndex = muxer?.addTrack(format)!!
                muxer?.start()
                isMuxerStarted = true
            }

        })
        Log.d(TAG, "codec info:" + encoder!!.codecInfo)
        val colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat)
        format.setInteger(MediaFormat.KEY_BIT_RATE, 2_000_000)
        format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE)
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
        Log.d(TAG, "format:$format")
        encoder!!.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

        inputSurface = InputSurface(encoder!!.createInputSurface())
        inputSurface?.makeCurrent()

        render = TextureRender()

        val outputDir = context.cacheDir
        val outputName = "name1.mp4"
        val outputFile = File(outputDir, outputName)
        muxer = MediaMuxer(outputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

        encoder?.start()



    }

    fun draw(bitmap: Bitmap) {
//        if (encoder == null) return

        render?.draw(viewportWidth, viewportHeight, bitmap, getMvp())
        val nanoPts = computePresentationTime(inputFrameIndex) * 1000
        inputSurface?.setPresentationTime(nanoPts)
        inputSurface?.swapBuffers()
        Thread.sleep(10)
        inputFrameIndex++
    }

    fun stop() {
        encoder?.stop()
        muxer?.stop()
        encoder?.release()
        encoder = null
    }

    private fun computePresentationTime(frameIndex: Int): Long {
        val t = (frameIndex * 1000_000 / FRAME_RATE).toLong()
        return t
    }

    fun getMvp(): FloatArray {
        val mvp = FloatArray(16)
        Matrix.setIdentityM(mvp, 0)
        Matrix.scaleM(mvp, 0, 1f, -1f, 1f)
        return mvp
    }

}